﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using GreyMagic;

namespace Hyperion
{
    /// <summary>
    /// Provides functionality for reading and writing from and to external process' memory space.
    /// </summary>
    public unsafe class ExternalProcessMemory : Memory
    {
        public ExternalProcessMemory(Process proc)
            : base(proc)
        { }

        #region Memory Overrides

        /// <summary>
        /// Reads the specified address and Marshals it into the specified Type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="address">The address.</param>
        /// <param name="isRelative">if set to <c>true</c> [is relative].</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public override T Read<T>(IntPtr address, bool isRelative = false)
        {
            var bytes = ReadBytes(address, MarshalCache<T>.Size, isRelative);

            fixed (byte* b = bytes)
                return (T)Marshal.PtrToStructure(new IntPtr(b), typeof(T));
        }

        /// <summary>
        /// Reads the specified addresses and Marshals it into the specified Type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="isRelative">if set to <c>true</c> [is relative].</param>
        /// <param name="addresses">The addresses.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public override T Read<T>(bool isRelative = false, params IntPtr[] addresses)
        {
            IntPtr tempAddress = addresses.Aggregate(IntPtr.Zero, (current, t) => Read<IntPtr>(current + (int)t));

            return Read<T>(tempAddress);
        }

        /// <summary>
        /// Writes to the specified address.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="address">The address.</param>
        /// <param name="value">The value.</param>
        /// <param name="isRelative">if set to <c>true</c> [is relative].</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public override int Write<T>(IntPtr address, T value, bool isRelative = false)
        {
            byte[] buffer = new byte[MarshalCache<T>.Size];

            fixed (byte* b = buffer)
                Marshal.StructureToPtr(value, new IntPtr(b), true);

            return WriteBytes(address, buffer, isRelative);
        }

        /// <summary>
        /// Allocates a piece of memory of the specified size.
        /// </summary>
        /// <param name="size">The size.</param>
        /// <returns></returns>
        public AllocatedMemory Allocate(int size)
        {
            return new AllocatedMemory(this, size);
        }

        #endregion
    }
}